`define BAUDGEN_PRESCALER 2
`define STATE_INACTIVE  0
`define STATE_IDLE 		1
`define STATE_CAPTURE 	2
`define STATE_COMM 		3
`define STATE_CLOSE 	4

module SPI_Module (
/* Interface A: System Signals */
A_CLK, A_RST, 
/* Interface B: Signals to Upper Layer */
B_DATAPAR_MISO,B_DATAPAR_MOSI,B_START,B_DATA_READY,B_TRANSACTION_ACTIVE,
/* Interface C: Signals to Lower Layer */
C_MOSI,C_MISO,C_SS,C_SPCLK
); 

input			A_CLK;
input			A_RST;
input [7:0] 	B_DATAPAR_MOSI;
output [7:0]	B_DATAPAR_MISO;
input 			B_START;
output			B_DATA_READY;
input			B_TRANSACTION_ACTIVE;
output			C_MOSI;
input			C_MISO;
output			C_SS;
output			C_SPCLK;

/* Registering outputs */
reg [7:0]	B_DATAPAR_MISO;
reg			B_DATA_READY;
reg			C_MOSI;
reg			C_SS; 
reg			C_SPCLK;

/* Variables used in the system */
reg [15:0] rBaudGenCounter;
reg rStart_Latch;
reg [7:0] rB_DATAPAR_MISO_Latch;
reg [7:0] rB_DATAPAR_MOSI_Latch;
reg [3:0] rState;
reg [2:0] rSPI_Transactions;
reg rC_SPCLK_Latch;


/* Variables Initialization */
initial
begin
	rBaudGenCounter <= 0;
	B_DATAPAR_MISO <= 8'h00;
	B_DATA_READY <= 0;
	C_MOSI <= 0;
	C_SS <= 1;
	C_SPCLK <= 0;
	rC_SPCLK_Latch <= 0;
	rStart_Latch <= 0;
	rState <= `STATE_IDLE;
	rSPI_Transactions <= 0;
	rB_DATAPAR_MISO_Latch <= 8'h00;
	rB_DATAPAR_MOSI_Latch <= 8'h00;
end

/* Baudrate generation */
always @(A_CLK)
begin
	rBaudGenCounter = rBaudGenCounter + 1;
	if (rBaudGenCounter == `BAUDGEN_PRESCALER)
	begin
		rBaudGenCounter = 0;
		rC_SPCLK_Latch <= ~rC_SPCLK_Latch; 
		if (rState == `STATE_COMM)	C_SPCLK <= ~C_SPCLK;
		else C_SPCLK <= 0;
	end 
end

always @ (posedge A_CLK)
begin
	/* Restore output transition signals to default value */
	B_DATA_READY <= 0;
	
	/* Start Condition detection */
	if (B_TRANSACTION_ACTIVE == 1) if (B_START == 1)	rStart_Latch <= 1;
	/* Main State Machine */
	case (rState) 
	`STATE_INACTIVE:
	begin
		C_SS <= 1;
		if (B_TRANSACTION_ACTIVE == 1)
		begin
			if (B_START == 1)	rStart_Latch <= 1;
			rState = `STATE_IDLE;
			C_SS <= 0;
		end
	end
	`STATE_IDLE: 
	begin 
		if (B_TRANSACTION_ACTIVE == 0)
			rState <= `STATE_INACTIVE;
		else if (rStart_Latch == 1)	
		begin 
			rState <= `STATE_CAPTURE;
			rStart_Latch <= 0;
			
			rB_DATAPAR_MISO_Latch <= 8'h00;
			rB_DATAPAR_MOSI_Latch <= B_DATAPAR_MOSI;

		end
	end
	
	`STATE_CAPTURE:
	begin
		rState <= `STATE_COMM;
		C_SS <= 0;
		C_MOSI <= rB_DATAPAR_MOSI_Latch[0];
	end
	
	`STATE_COMM:
	begin
		if (rSPI_Transactions == 7)
		begin
			rState <= `STATE_CLOSE;
			rSPI_Transactions <= 0;
			B_DATAPAR_MISO <= rB_DATAPAR_MISO_Latch;
			
		end
	end
	
	`STATE_CLOSE:
	begin
		B_DATA_READY <= 1;
		if (rStart_Latch == 1)	
			rState <= `STATE_CAPTURE;
		else
			rState <= `STATE_IDLE;
	end
	endcase
end

/* Shifting Output data */
always @ (negedge C_SPCLK)
begin
	if (rState == `STATE_COMM)
	begin
		rB_DATAPAR_MOSI_Latch = {1'b0,rB_DATAPAR_MOSI_Latch[7:1]};
		C_MOSI = rB_DATAPAR_MOSI_Latch[0];
		rSPI_Transactions = rSPI_Transactions + 1;
	end
end

/* Capturing Input data */
always @ (posedge C_SPCLK)
begin
	if (rState == `STATE_COMM)
	begin
		rB_DATAPAR_MISO_Latch[rSPI_Transactions] <= C_MISO;
	end
end
endmodule
